<template>
  <div
    class="template-form"
    :class="[
      {
        'template-form--inline': inline,
        clearfloat: inline,
        'border-form': isBorder
      },
      formClass
    ]"
  >
    <el-form
      v-if="!isLoading"
      :label-width="labelWidth"
      :label-position="labelPosition"
      @submit.native.prevent
    >
      <template
        v-for="{
          prop,
          label,
          type,
          enums,
          props,
          required,
          editComponent,
          pickerOptions,
          dateFormat,
          isFull,
          disabled,
          placeholder,
          inputClass,
          rows,
          maxlength,
          border,
          min,
          max,
          step,
          width,
          displayProp,
          formItemWidth,
          formItemInline,
          singleton,
          delimiter,
          tips,
          labelTips,
          saveProp = true,
          ...arg
        } in configs"
      >
        <el-form-item
          :key="prop"
          :required="required && !isQuery"
          :label="label"
          :label-width="width"
          :style="{ width: formItemWidth || defaultFormItemWidth }"
          :class="{
            'template-form--inline-block': formItemInline || inlineItem
          }"
        >
          <template slot="label">
            <slot :name="`${prop}-label`">
              {{ label }}
              <el-tooltip v-if="labelTips" effect="light" placement="top">
                <i class="el-icon-question" />
                <div slot="content" v-html="labelTips" />
              </el-tooltip>
            </slot>
          </template>
          <slot
            :name="prop"
            :data="{
              prop,
              label,
              type,
              enums,
              props,
              required,
              disabled,
              placeholder,
              inputClass,
              rows,
              maxlength,
              border,
              min,
              max,
              ...arg
            }"
          >
            <component
              :is="editComponent"
              v-if="editComponent"
              :ref="prop"
              v-model="params[prop]"
              :prop="prop"
              :enums="enums"
              :props="props"
              :class="inputClass"
              :data="isFull ? params : params[prop]"
              :value-display="params[displayProp]"
              :default-datas="data"
              :disabled="disabled"
              :placeholder="placeholder"
              :max="max"
              :min="min"
              v-bind="{
                ...$attrs,
                ...arg
              }"
              @input="handleInput(prop, $event, isFull)"
              @update:data="handleInput(prop, $event, isFull)"
            />
            <el-select
              v-else-if="
                (type === 'multiple-select' || type === 'select') && enums
              "
              v-model="params[prop]"
              filterable
              class="select"
              clearable
              :class="inputClass"
              :multiple="type === 'multiple-select'"
              :collapse-tags="!arg.notCollapseTags"
              :disabled="disabled"
              :placeholder="placeholder || `请选择${label}`"
            >
              <el-option
                v-for="{ label: eLabel, value } in enums"
                :key="value"
                :value="value"
                :label="eLabel"
              />
            </el-select>
            <el-checkbox-group
              v-else-if="type === 'checkbox' && enums"
              v-model="params[prop]"
              :class="inputClass"
              :disabled="disabled"
            >
              <el-checkbox
                v-for="{ label: eLabel, value } in enums"
                :key="value"
                :label="eLabel"
                :border="border"
              >{{ eLabel }}</el-checkbox>
            </el-checkbox-group>
            <el-radio-group
              v-else-if="type === 'radio' && enums"
              v-model="params[prop]"
              :class="inputClass"
              :disabled="disabled"
              :placeholder="placeholder"
            >
              <el-radio
                v-for="{ label: eLabel, value } in enums"
                :key="value"
                :label="value"
                :border="border"
              >{{ eLabel }}</el-radio>
            </el-radio-group>
            <date-time-range-group
              v-else-if="type === 'datetimerangegroup'"
              :class="inputClass"
              :value="params[prop]"
              :start-placeholder="arg.startPlaceholder || '开始日期'"
              :end-placeholder="arg.endPlaceholder || '结束时间'"
              @input="handleDateTimeRangePick(prop, props, $event)"
            />
            <el-date-picker
              v-else-if="type === 'datetimerange' && Array.isArray(props)"
              :class="inputClass"
              :value="
                saveProp ? params[prop] : [params[props[0]], params[props[1]]]
              "
              type="datetimerange"
              :default-time="['00:00:00', '23:59:59']"
              :range-separator="arg.rangeSeparator || '-'"
              :start-placeholder="arg.startPlaceholder || '开始时间日期'"
              :end-placeholder="arg.endPlaceholder || '结束时间日期'"
              :picker-options="pickerOptions"
              :disabled="disabled"
              @input="handleDateTimeRangePick(prop, props, $event, saveProp)"
            />
            <el-date-picker
              v-else-if="type === 'date'"
              v-model="params[prop]"
              :class="inputClass"
              type="date"
              :picker-options="pickerOptions"
              :disabled="disabled"
              :value-format="dateFormat"
              :placeholder="placeholder || '选择日期'"
            />
            <el-date-picker
              v-else-if="type === 'datetime'"
              :value="params[prop]"
              :class="inputClass"
              type="datetime"
              :picker-options="pickerOptions"
              :disabled="disabled"
              :value-format="dateFormat"
              :placeholder="placeholder || '选择日期时间'"
              @input="handleDateTimeInput($event, prop, { ...arg, ...$attrs })"
            />
            <el-input-number
              v-else-if="type === 'number'"
              v-model="params[prop]"
              v-bind="arg"
              :min="min"
              :max="max"
              :placeholder="placeholder || `请输入${label}`"
              :step="step || 1"
              controls-position="right"
              :disabled="disabled"
            />
            <el-input
              v-else-if="type === 'textarea'"
              v-model="params[prop]"
              v-bind="arg"
              type="textarea"
              :rows="rows"
              :maxlength="maxlength"
              show-word-limit
              :class="inputClass"
              :placeholder="placeholder || `请输入${label}`"
              :disabled="disabled"
            />
            <el-input
              v-else-if="type === 'password'"
              v-model="params[prop]"
              type="password"
              :class="inputClass"
              :maxlength="maxlength"
              show-word-limit
              :placeholder="placeholder || `请输入${label}`"
              :disabled="disabled"
            />
            <el-color-picker
              v-else-if="type === 'color'"
              v-model="params[prop]"
              size="medium"
            />
            <template v-else-if="type === 'only-text'">
              <span v-if="displayProp && params[displayProp]">{{
                params[displayProp]
              }}</span>
              <span v-else-if="enums && Array.isArray(params[prop])">{{
                listEnumsValue(enums, params[prop], sep) | mapDefault
              }}</span>
              <!--使用枚举显示字段信息-->
              <span v-else-if="enums">{{
                getEnumsValue(enums, params[prop]) | mapDefault
              }}</span><span v-else>
                {{ params[prop] || '-' }}
              </span></template>
            <el-input
              v-else
              v-model="params[prop]"
              type="text"
              :class="inputClass"
              :maxlength="maxlength"
              show-word-limit
              :placeholder="placeholder || `请输入${label}`"
              :disabled="disabled"
            />
            <el-tooltip
              v-if="tips"
              effect="dark"
              :content="tips"
              placement="top"
              class="template-form-content__tips"
              :class="{ 'vertical-top': type === 'textarea' }"
            >
              <i class="el-icon-question" />
            </el-tooltip>
          </slot>
        </el-form-item>
      </template>
    </el-form>
    <el-form
      :label-width="labelWidth"
      :label-position="labelPosition"
      @submit.native.prevent
    >
      <slot />
    </el-form>
  </div>
</template>

<script>
import LoadingMixin from 'modules/mixins/section-loading'
import DateTimeRangeGroup from 'modules/component/DateTimeRangeGroup'
import moment from 'moment'
import { isFunction } from 'lodash'
import {
  isFullEmpty,
  filterEmpty,
  validateWithMessage,
  filterNotInConfigs
} from 'modules/utils/params'

const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss'

export default {
  name: 'TemplateForm',
  components: {
    DateTimeRangeGroup
  },
  mixins: [LoadingMixin],
  inheritAttrs: false,
  props: {
    isBorder: {
      type: Boolean,
      default: false
    },
    isQuery: {
      type: Boolean,
      default: false
    },
    isFilterEmpty: {
      type: Boolean,
      default: true
    },
    inline: {
      type: Boolean,
      default: false
    },
    configs: {
      type: Array,
      default() {
        return []
      }
    },
    params: {
      type: Object,
      default() {
        return {}
      }
    },
    data: {
      type: Object,
      default() {
        return {}
      }
    },
    labelWidth: {
      type: String,
      default: '100px'
    },
    labelPosition: {
      type: String,
      default: 'right'
    },
    defaultFormItemWidth: {
      type: String,
      default: '100%'
    },
    formClass: {
      type: String,
      default: ''
    },
    inlineItem: Boolean,
    // 过滤空值和 trim空值
    isTrim: {
      type: Boolean,
      default: true
    },
    isFilterConfigs: {
      type: Boolean,
      default: false
    }
  },
  created() {
    // 确保数据处理正确后，再渲染组件，避免组件错误使用默认值
    this.loading()
    this.registerParams()
    this.$nextTick(() => {
      this.loaded()
    })
  },
  methods: {
    registerParams() {
      // 为所有没有复合的props的字段注册监听
      this.configs
        .filter(({ props }) => !props)
        .forEach(({ prop }) => {
          this.params[prop] === undefined &&
            this.$set(this.params, prop, undefined)
        })
      // 注册复合props,并添加对应关系
      this.configs
        .filter(({ props }) => props)
        .forEach(({ props }) => {
          props.forEach(prop => {
            this.params[prop] === undefined &&
              this.$set(this.params, prop, undefined)
          })
        })
    },
    resetParams() {
      this.configs.forEach(({ prop, props = [], defaultValue }) => {
        const keys = props.includes(prop) ? props : props.concat(prop)
        keys.forEach((prop, index) => {
          // 优先获取路径参数
          const uriValue = this.$route.query[prop]
          // 复合条件时优先考虑uriValue再挨个赋值defaultValue
          const value = !isFullEmpty(uriValue)
            ? uriValue
            : Array.isArray(defaultValue) && props.length > 0
              ? defaultValue[index]
              : defaultValue

          // 忽略undefined和null的深拷贝
          this.$set(
            this.params,
            prop,
            value === undefined ? undefined : JSON.parse(JSON.stringify(value))
          )
        })
      })
      this.$emit('reset')
      // 动态组件重置
      const refs = this.$refs || {}
      Object.values(refs)
        .filter(ref => ref && ref[0] && isFunction(ref[0].reset))
        .forEach(ref => {
          ref[0] && ref[0].reset()
        })
    },
    submit() {
      this.$emit('before-submit')
      // 动态组件提交
      const refs = this.$refs || {}
      Object.values(refs)
        .filter(ref => ref && ref[0] && isFunction(ref[0].submit))
        .forEach(ref => {
          ref[0] && ref[0].submit()
        })
      // 查询模式直接返回参数，表单模式需要验证信息
      if (
        this.isQuery ||
        validateWithMessage(this.configs, this.params, this.data)
      ) {
        this.$emit('submit')
        const params = this.isFilterEmpty
          ? filterEmpty(this.params, this.isTrim)
          : this.params
        return this.isFilterConfigs
          ? filterNotInConfigs(this.configs, params)
          : params
      }
    },
    handleDateTimeRangePick(
      timeKey,
      [startTimeKey, endTimeKey],
      range,
      saveProp = true
    ) {
      let [startTime, endTime] = range || [undefined, undefined]
      // 将date格式转为字符串
      startTime = startTime && moment(startTime).format(DATE_FORMAT)
      endTime = endTime && moment(endTime).format(DATE_FORMAT)
      this.$set(this.params, startTimeKey, startTime)
      this.$set(this.params, endTimeKey, endTime)
      saveProp && this.$set(this.params, timeKey, [startTime, endTime])
    },
    handleInput(prop, val, isFull) {
      // 多字段暂不支持自动input
      if (isFull) {
        this.$emit('change')
        return
      }
      const param = this.params[prop]
      if (Array.isArray(param)) {
        param.splice(0, param.length)
        param.splice(0, 0, ...val)
      } else {
        this.$set(this.params, prop, val)
      }
      this.$emit('change', prop, val)
    },
    handleDateTimeInput(value, prop, options) {
      if (!options.groupType) {
        this.$set(this.params, prop, value)
        return
      }
      const { groupType, startProp = '', endProp = '' } = options
      if (groupType === 'start') {
        const endValue = this.params[endProp]
        if (endValue && moment(value).isAfter(moment(endValue))) {
          return
        }
        if (value) {
          this.$set(this.params, prop, value)
        } else {
          this.$set(this.params, prop, '')
        }
      }
      if (groupType === 'end') {
        const startValue = this.params[startProp]
        if (startValue && moment(startValue).isAfter(moment(value))) {
          return
        }
        if (value) {
          this.$set(this.params, prop, value)
        } else {
          this.$set(this.params, prop, '')
        }
      }
    }
  }
}
</script>

<style lang="scss">
.template-form {
  --form-item-width: 96%;
}
.template-form--inline > .el-form {
  float: left;
}
.template-form--inline-block {
  display: inline-block;
}
.template-form .el-form {
  .el-textarea,
  .el-input,
  .el-input-number,
  .el-date-editor--datetimerange,
  .el-autocomplete {
    width: var(--form-item-width);
  }
  .el-textarea__inner,
  .el-select {
    width: 100%;
  }
}
.template-form .el-form-item__content {
  .el-icon-question {
    margin-left: 5px;
  }
  .vertical-top {
    vertical-align: top;
    margin-top: 5px;
  }
}
.template-form {
  .el-color-picker {
    display: block;
  }
  &.border-form {
    .el-form-item__content {
      border-left: 1px solid #e8e8e8;
      padding: 10px;
    }
    .el-form-item {
      margin-bottom: 0;
      padding: 0;
      border: 1px solid #e8e8e8;
      border-top: none;
      position: relative;
      &:first-child {
        border-top: 1px solid #e8e8e8;
      }
    }
    .el-form-item__label {
      padding: 10px 12px 0 0;
      position: absolute;
      height: 100%;
      background: #fafafa;
      color: #909399;
    }
  }
}
</style>
